/** 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.tompai.common.http;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContexts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @desc: datagather-tools
 * @name: HttpClientUtil.java
 * @author: tompai
 * @createTime: 2018年5月3日 下午4:08:12
 * @history:
 * @version: v1.0
 */

public class HttpClientUtil {
	public static final Logger LOG = LoggerFactory.getLogger(HttpClientUtil.class);
	// 依次是代理地址，代理端口号，用户密码
	private static String proxyHost = "127.0.0.1";
	private static int proxyPort = 1080;
	// private static String proxyName="user";
	// private static String proxyPwd="userPwd";

	public static String getWithProxy(String url, Map<String, String> headers, String charset) {
		// 用户名和密码验证
		/*
		 * Authenticator.setDefault(new Authenticator(){ protected
		 * PasswordAuthentication getPasswordAuthentication(){ PasswordAuthentication
		 * p=new PasswordAuthentication(proxyName, proxyPwd.toCharArray()); return p; }
		 * });
		 */
		Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", new MyConnectionSocketFactory())
				.register("https", new MySSLConnectionSocketFactory(SSLContexts.createSystemDefault())).build();
		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg, new FakeDnsResolver());

		/////////////////
		CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(cm).build();
		InetSocketAddress socksaddr = new InetSocketAddress(proxyHost, proxyPort);
		HttpClientContext context = HttpClientContext.create();
		context.setAttribute("socks.address", socksaddr);
		HttpGet httpget = new HttpGet(url);
		if (headers != null) {
			for (String key : headers.keySet()) {
				httpget.setHeader(key, headers.get(key));
			}
		}
		// 设置超时,不设置超时会出现长期卡顿的现象//
		// 一旦服务器没有响应，等待时间N久(>24小时)。
		RequestConfig defaultConfig = RequestConfig.custom()
				.setCookieSpec(CookieSpecs.STANDARD)
				.setSocketTimeout(20000) // 
				.setConnectTimeout(6000) // 设置建立连接后，到得到数据前的等待超时时间
				.setConnectionRequestTimeout(6000)// 表示从连接池中后去连接的timeout时间
				.setExpectContinueEnabled(false)// 设置失败后不再重试
				.setContentCompressionEnabled(true)// 设置压缩
				.build();

		httpget.setConfig(defaultConfig);
		String bufferStr = null;
		try {
			// 执行get请求
			LOG.info("begain entity...");
			CloseableHttpResponse response = httpclient.execute(httpget, context);
			// 获取响应消息实体
			LOG.info("get entity...");
			try {
				HttpEntity entity = response.getEntity();
				int statusCode = response.getStatusLine().getStatusCode();
				if (entity != null) {
					// HttpStatus.SC_NOT_FOUND//404
					if (statusCode == HttpStatus.SC_OK) {
						InputStream inputStream = entity.getContent();
						LOG.info("entity length: \t" + entity.getContentLength());
						bufferStr=IOUtils.toString(inputStream,charset);
						inputStream.close();
						//LOG.info("get entity end.");
					} else {
						LOG.error("返回码:\t" + statusCode + " ,response Content failed.");
					}
				}else {
					LOG.error("返回码:\t" + statusCode + " ,response Entity failed.");
				}
			} finally {
				if (null != response) {
					response.close();
				}
			}
		} catch (ClientProtocolException e1) {
			e1.printStackTrace();
			LOG.error(e1.getMessage());
		} catch (IOException e2) {
			e2.printStackTrace();
			LOG.error(e2.getMessage());
		} finally {
			try {
				// 释放 links
				httpget.releaseConnection();
				// 释放 httpclient pool;
				httpclient.close();
				//LOG.info("have releaseConnection.");
			} catch (Exception e3) {
				e3.printStackTrace();
				LOG.error(e3.getMessage());
			}
		}
		return bufferStr;
	}

	static class FakeDnsResolver implements DnsResolver {
		@Override
		public InetAddress[] resolve(String host) throws UnknownHostException {
			// Return some fake DNS record for every request, we won't be using it
			return new InetAddress[] { InetAddress.getByAddress(new byte[] { 1, 1, 1, 1 }) };
		}
	}

	static class MyConnectionSocketFactory extends PlainConnectionSocketFactory {
		@Override
		public Socket createSocket(final HttpContext context) throws IOException {
			InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
			Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
			return new Socket(proxy);
		}

		@Override
		public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress,
				InetSocketAddress localAddress, HttpContext context) throws IOException {
			// Convert address to unresolved
			InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(host.getHostName(),
					remoteAddress.getPort());
			return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
		}
	}

	static class MySSLConnectionSocketFactory extends SSLSocketFactory {// SSLConnectionSocketFactory

		public MySSLConnectionSocketFactory(final SSLContext sslContext) {
			// You may need this verifier if target site's certificate is not secure
			super(sslContext, ALLOW_ALL_HOSTNAME_VERIFIER);
			// SSLConnectionSocketFactory.
		}

		@Override
		public Socket createSocket(final HttpContext context) throws IOException {
			InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
			Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
			return new Socket(proxy);
		}

		@Override
		public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress,
				InetSocketAddress localAddress, HttpContext context) throws IOException {
			// Convert address to unresolved
			InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(host.getHostName(),
					remoteAddress.getPort());
			return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
		}
	}

}
